﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using SuperX.Common.Helper;
using SuperX.Common.Log;
using SuperSocket.ClientEngine;
using SuperSocket.ProtoBase;
using ErrorEventArgs = SuperSocket.ClientEngine.ErrorEventArgs;
using SuperX.SuperSocket.Socket.Message;
using SuperX.SuperSocket.Socket.Command;

namespace SuperX.SuperSocket.Socket.Client
{
    public class SocketClient : ISocketClient
    {
        private readonly SocketClientConfig _superSocketConfig;

        /// <summary>
        /// 心跳检查定时器
        /// </summary>
        private Timer _timeHeartBeat = null;

        private int _connecting;

        private EasyClient _client = null;

        /// <summary>
        /// 冗余专用，Connect方法只允许主动调用(默认是false)
        /// </summary>
        public bool MinConnect { get; set; }

        /// <summary>
        /// 给使用者自定义的客户端连接成功后的事件。
        /// </summary>
        public event EventHandler Connected;

        /// <summary>
        /// 给使用者自定义断开连接的事件。
        /// </summary>
        public event EventHandler DisConnected;

        public event EventHandler<MessageArgs> MessageRecevied;

        public event EventHandler<SocketErrorArgs> Error;

        public string Ipaddress { get; private set; }

        public int Port { get; private set; }

        public bool IsConnected
        {
            get
            {
                if (_client == null)
                {
                    return false;
                }

                return _client.IsConnected;
            }
        }

        public SocketClient(SocketClientConfig superSocketConfig)
        {
            if (superSocketConfig.Role == Role.Server)
            {
                return;
            }
            Ipaddress = superSocketConfig.Ip;
            Port = superSocketConfig.Port;
            _superSocketConfig = superSocketConfig;
        }

        /// <summary>
        /// 连接
        /// </summary>
        public void Init()
        {
            if (_superSocketConfig.Role != Role.Client)
            {
                return;
            }

            try
            {
                //AsyncTcpSession client = new AsyncTcpSession(new IPEndPoint(IPAddress.Parse(ip), port));
                //初始化并启动客户端引擎（TCP、文本协议）
                _client = new EasyClient();
                _client.ReceiveBufferSize = 1024 * 1024;
                _client.Connected += SocketConnected;
                _client.Closed += DisConnect;
                //_client.Error += _client_Error;
                _client.Error += ErrorHandler;
                _client.Initialize(new MessageClientReceiveFilter(), DataHandler);
            }
            catch
            {
            }
        }

        private void _client_Error(object sender, ErrorEventArgs e)
        {
            Logger.Error(string.Format("From Client: client error! IP: {0}, Port: {1}  reason {2}.", Ipaddress, Port, e.Exception?.ToString()));
        }

        private void DataHandler(IPackageInfo obj)
        {
            if (obj is MessagePackageInfo messagePackageInfo)
            {
                var socketMessage = messagePackageInfo.Body;
                MessageRecevied?.Invoke(this, new MessageArgs(null, socketMessage)
                {
                    AppMessage = new XSocketMessage()
                    {
                        Datas = socketMessage.Body
                    },
                });
            }
        }

        /// <summary>
        /// 冗余专用，正常调用使用Connect方法
        /// </summary>
        /// <returns></returns>
        public Task<bool> ConnectAsync()
        {
            if (IsConnected)
            {
                return null;
            }
            if (!IPAddress.TryParse(_superSocketConfig.Ip, out var iPAddress))
            {
                return null;
            }

            if (_client == null)
            {
                Init();
            }
            if (_client == null)
            {
                return null;
            }
            return _client.ConnectAsync(new IPEndPoint(iPAddress, _superSocketConfig.Port));
        }

        public bool Connect()
        {
            if (IsConnected)
            {
                return true;
            }
            if (Interlocked.CompareExchange(ref _connecting, 1, 0) == 1)
            {
                return false;
            }
            if (!IPAddress.TryParse(_superSocketConfig.Ip, out var iPAddress))
            {
                return false;
            }

            if (_client == null)
            {
                Init();
            }
            if (_client == null)
            {
                return false;
            }
            try
            {
                var flag = false;
                Logger.Trace($"begin ConnectAsync {_superSocketConfig.Ip}:{_superSocketConfig.Port}");
                var task = _client.ConnectAsync(new IPEndPoint(iPAddress, _superSocketConfig.Port));
                task.Wait(5000);
                if (!task.IsCompleted)
                {
                    DisConnect();
                }
                else
                {
                    flag = task.Result;
                }
                Logger.Trace($"end ConnectAsync {_superSocketConfig.Ip}:{_superSocketConfig.Port} {flag}");
                return flag;
            }
            finally
            {
                _connecting = 0;
            }
        }

        /// <summary>
        /// 连接成功
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SocketConnected(object sender, EventArgs e)
        {
            Logger.Info("From Client: client connected! ");
            if (_superSocketConfig.IsEnableCheckHeartbeat && _timeHeartBeat == null)
            {
                _timeHeartBeat = new Timer(HeartBeatCallBack, null, _superSocketConfig.MHeartBeatInterval, _superSocketConfig.MHeartBeatInterval);
            }
            Connected?.Invoke(sender, e);
        }

        /// <summary>
        /// 断开连接时发生的回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisConnect(object sender, EventArgs e)
        {
            Logger.Info($"From Client: disconnected!");
            DisposeTimer();
            DisConnected?.Invoke(sender, e);
        }

        /// <summary>
        /// 主动中断连接
        /// </summary>
        public void DisConnect()
        {
            DisposeTimer();
            CloseClient();
        }

        private void DisposeTimer()
        {
            if (_timeHeartBeat != null)
            {
                _timeHeartBeat.Dispose();
                _timeHeartBeat = null;
            }
        }

        private void CloseClient()
        {
            if (_client != null)
            {
                _client.Connected -= SocketConnected;
                _client.Closed -= DisConnect;
                _client.Error -= ErrorHandler;
                _client.Close().Wait(25000);
                _client = null;
            }
        }

        /// <summary>
        /// 客户端发生错误时获取错误信息。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ErrorHandler(object sender, ErrorEventArgs e)
        {
            var reslut = string.Empty;
            if (e.Exception.GetType() != typeof(SocketException))
            {
                reslut = $"错误:{e.Exception.Message}";
            }
            else
            {
                var socketException = e.Exception as SocketException;
                reslut = socketException != null && socketException.SocketErrorCode == SocketError.ConnectionRefused ?
                    "错误:请先启动AppServer 服务端！" :
                    $"错误:{e.Exception.Message}";
            }
            reslut += string.Format(" IP: {0}, Port: {1}", Ipaddress, Port);
            Logger.Error(reslut);
            Error?.Invoke(sender, new SocketErrorArgs() { Error = reslut, Exception = e.Exception });
        }

        /// <summary>
        /// 定时发送心跳包
        /// </summary>
        /// <param name="state"></param>
        private void HeartBeatCallBack(object state)
        {
            try
            {
                _timeHeartBeat?.Change(Timeout.Infinite, Timeout.Infinite);
                if (_superSocketConfig.IsReConnect && _client != null && !_client.IsConnected)
                {
                    try
                    {
                        if (!MinConnect)
                        {
                            Connect();
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Trace(ex.Message);
                    }
                }
                if (_client != null && _client.IsConnected)
                {
                    Send("HeartBeat", InnerMessage.Alive.ToString());
                }
            }
            finally
            {
                _timeHeartBeat?.Change(_superSocketConfig.MHeartBeatInterval, _superSocketConfig.MHeartBeatInterval);
            }
        }

        public void Send(byte[] datas, string header = null)
        {
            SendMessage(new SocketMessage() { Header = header, Body = datas });
        }

        public void Send(string content)
        {
            Send(string.Empty, content);
        }

        public void Send(string header, string content)
        {
            var appMsg = new XSocketMessage() { Content = content };
            SendMessage(new SocketMessage() { Header = header, Body = appMsg.Datas });
        }

        public void Send<T>(T obj) where T : new()
        {
            var appMsg = new XSocketMessage<T>(obj);
            SendMessage(new SocketMessage() { Body = appMsg.Datas });
        }

        public void SendMessage(SocketMessage message)
        {
            try
            {
                if (string.IsNullOrEmpty(message.Header))
                {
                    message.Header = "Monitor";
                }
                if (MinConnect)
                {
                    _client.Send(message.GetSendBytes());
                }
                else
                {
                    if (Connect())
                    {
                        _client.Send(message.GetSendBytes());
                    }
                }
            }
            catch (Exception ex)
            {
                try
                {
                    Logger.Info(JsonHelper.JsonSerialize(message));
                }
                catch (Exception e)
                {
                    Logger.ErrorException(e);
                }

                Logger.ErrorException(ex);
            }
        }

        public void Send(Dictionary<string, string> headers, string content)
        {
            var appMsg = new XSocketMessage() { Content = content };
            SendMessage(new SocketMessage() { Headers = headers, Header = CommandTransfer.Serialize(headers), Command = "SendObjectWithHeaders", Body = appMsg.Datas });
        }

        public void Send(Dictionary<string, string> headers, byte[] datas)
        {
            SendMessage(new SocketMessage() { Headers = headers, Header = CommandTransfer.Serialize(headers), Command = "SendObjectWithHeaders", Body = datas });
        }
    }
}